﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Web;
using System.Reflection;

namespace Gtkj.CMS.Common
{
    /// <summary>
    /// 用于实现指定的方法的缓存操作，应与DataCacheAttribute结合使用（技巧提示：若要删除缓存可指定method为返回null（Invoke方法）或null列表（InvokeList方法））
    /// </summary>
    public class CacheInvoker
    {
        /// <summary>
        /// 用于缓存指定方法，获取计算或缓存结果
        /// </summary>
        /// <typeparam name="T">缓存结果类型，要求为class</typeparam>
        /// <param name="obj">请传this对象</param>
        /// <param name="methodName">缓存的方法名，请传调用方法的方法名</param>
        /// <param name="method">原始处理方法，对于异常数据不应抛出错误，应返回null</param>
        /// <param name="params">用于拼接缓存key的额外参数，每个字段请使用基本类型</param>
        /// <param name="refreshing">强制更新缓存（强制调用原始处理方法返回结果并再次缓存）</param>
        /// <param name="caching">进行缓存（若为false，只从缓存中取值，对于未缓存的新数据不会加入缓存）</param>
        /// <returns>计算或缓存结果</returns>
        public static T Invoke<T>(object obj, string methodName, Func<T> method, object @params = null, bool refreshing = false, bool caching = true) where T : class
        {            
            //通过反射获取类名，并定位方法，取得DataCacheAttribute特性
            var targetType = obj.GetType();
            var targetInfo = targetType.GetMethod(methodName);
            //发现特性进行缓存计算、未发现特性则不缓存
            if (targetInfo.IsDefined(typeof(DataCacheAttribute), true))
            {
                var attr = targetInfo
                    .GetCustomAttributes(typeof(DataCacheAttribute), true)
                    .FirstOrDefault()
                    as DataCacheAttribute;
                //拼接标识（Key）
                StringBuilder sbKey = new StringBuilder();
                sbKey.Append(targetType.Name);
                sbKey.Append("-");
                sbKey.Append(methodName);

                if (@params != null)
                {
                    var pros = @params.GetType().GetProperties();
                    foreach (var pro in pros.OrderBy(item => item.Name))
                    {
                        var value = pro.GetValue(@params);
                        if (value != null)
                        {
                            sbKey.Append("-");
                            sbKey.Append(pro.Name);
                            sbKey.Append("-");
                            sbKey.Append(value.ToString());
                        }
                    }
                }

                string key = sbKey.ToString();
                //强制刷新缓存
                if (refreshing)
                {
                    var rsl = method.Invoke();
                    if (caching)
                    {
                        CacheContainer.SetData(key, rsl, attr.CacheSeconds);
                    }
                    return rsl;
                }
                else
                {
                    //获取缓存值
                    T data = CacheContainer.GetData<T>(key);
                    //已缓存的直接返回结果，未缓存的调用原始处理方法后返回处理结果并缓存
                    if (data != null)
                    {
                        //已缓存
                        return (T)data;
                    }
                    else
                    {
                        //未缓存
                        var rsl = method.Invoke();
                        //如果需要缓存
                        if (caching)
                        {
                            CacheContainer.SetData(key, rsl, attr.CacheSeconds);
                        }
                        return rsl;
                    }

                }
            }
            else
            {
                //未发现特性不使用缓存
                return method.Invoke();
            }
        }

        /// <summary>
        /// 用于缓存指定方法，获取计算后或缓存的与原始数据的一一对应的结果列表
        /// </summary>
        /// <typeparam name="T1">列表元素的结果类型，需为class</typeparam>
        /// <typeparam name="T2">列表元素的原始类型</typeparam>
        /// <param name="obj">请传this对象</param>
        /// <param name="methodName">缓存的方法名，请传调用方法的方法名</param>
        /// <param name="method">原始处理方法，要求处理对象为列表，且返回结果为列表，并且原始数据与结果一一对应，对于异常的数据应填充null</param>
        /// <param name="methodParam">待处理原始数据列表</param>
        /// <param name="params">用于拼接缓存key的额外参数，每个字段请使用基本类型</param>
        /// <param name="refreshing">强制更新缓存（强制调用原始处理方法返回结果）</param>
        /// <param name="caching">进行缓存（若为false，只从缓存中取值，对于未缓存的新数据不会加入缓存）</param>
        /// <returns>计算或缓存的与原始数据的一一对应的结果列表</returns>
        public static List<T1> InvokeList<T1, T2>(object obj, string methodName, Func<List<T2>, List<T1>> method, List<T2> methodParam, object @params = null, bool refreshing = false, bool caching = true) where T1 : class
        {
            //判断是否重载了ToString函数
            if (typeof(object).GetMethod("ToString").MethodHandle.Value.ToInt64() == (typeof(T2).GetRuntimeMethod("ToString", new Type[] { }).MethodHandle.Value.ToInt64()))
            {
                return null;
            }

            //通过反射获取类名，并定位方法，取得DataCacheAttribute特性
            var targetType = obj.GetType();
            var targetInfo = targetType.GetMethod(methodName);
            //发现特性进行缓存计算、未发现特性则不缓存
            if (targetInfo.IsDefined(typeof(DataCacheAttribute), true))
            {
                var attr = targetInfo
                    .GetCustomAttributes(typeof(DataCacheAttribute), true)
                    .FirstOrDefault()
                    as DataCacheAttribute;

                //拼接标识（Key）
                StringBuilder sbKeyPre = new StringBuilder();
                sbKeyPre.Append(targetType.Name);
                sbKeyPre.Append("-");
                sbKeyPre.Append(methodName);
                if (@params != null)
                {
                    var pros = @params.GetType().GetProperties();
                    foreach (var pro in pros.OrderBy(item => item.Name))
                    {
                        var value = pro.GetValue(@params);
                        if (value != null)
                        {
                            sbKeyPre.Append("-");
                            sbKeyPre.Append(pro.Name);
                            sbKeyPre.Append("-");
                            sbKeyPre.Append(value.ToString());
                        }
                    }
                }

                //强制更新缓存
                if (refreshing)
                {
                    var rsl = method.Invoke(methodParam);
                    //如果进行缓存
                    if (caching)
                    {
                        //所有调用原始方法计算并缓存的列表对应的缓存使用Key
                        List<string> keys = new List<string>();
                        for (int i = 0; i < methodParam.Count; i++)
                        {
                            var item = methodParam[i];
                            string key = sbKeyPre.ToString() + "-" + item.ToString();
                            keys.Add(key);
                        }

                        for (int i = 0; i < rsl.Count; i++)
                        {
                            //逐条缓存结果
                            CacheContainer.SetData(keys[i], rsl[i], attr.CacheSeconds);
                        }
                    }
                    return rsl;
                }
                else
                {
                    //最终返回结果
                    List<T1> rsl = new List<T1>();
                    //需要调用原始方法计算并缓存的列表（待处理列表）
                    List<T2> needSolv = new List<T2>();
                    //需要调用原始方法计算并缓存的列表对应的缓存使用Key
                    List<string> keys = new List<string>();
                    //需要调用原始方法计算并缓存的列表对应在最终结果中的序号位置
                    List<int> needIndexInRsl = new List<int>();

                    //逐个取缓存值，已缓存的直接填充最终结果，未缓存的在最终结果填充占位符，并增加至待处理列表，记录对应key（用于缓存）和对应最终结果的序号（用于填入最终结果）
                    for (int i = 0; i < methodParam.Count; i++)
                    {
                        var item = methodParam[i];
                        string key = sbKeyPre.ToString() + "-" + item.ToString();
                        //获取缓存值
                        T1 data = CacheContainer.GetData<T1>(key);
                        if (data != null)
                        {
                            //已缓存
                            rsl.Add((T1)data);
                        }
                        else
                        {
                            //未缓存
                            rsl.Add(default(T1));
                            needSolv.Add(item);
                            keys.Add(key);
                            needIndexInRsl.Add(i);
                        }
                    }

                    //如果有未缓存的数据
                    if (needSolv.Count > 0)
                    {
                        try
                        {
                            //调用原始方法，获取未缓存key列表的结果列表
                            var solv = method(needSolv);

                            for (int i = 0; i < solv.Count; i++)
                            {
                                //调用结果回填到最终结果空缺的相应位置
                                rsl[needIndexInRsl[i]] = solv[i];
                                //如果进行缓存
                                if (caching)
                                {
                                    //逐条缓存结果
                                    CacheContainer.SetData(keys[i], solv[i], attr.CacheSeconds);
                                }
                            }
                        }
                        catch
                        {
                            return null;
                        }
                    }
                    return rsl;
                }
            }
            else
            {
                //未发现特性不使用缓存
                return method.Invoke(methodParam);
            }
        }
    }
}